package com.cctc.juc.source.Athread.threadpool.util;

import java.util.concurrent.*;

/**
 * [线程池] 工厂类
 * [阿里巴巴 Java 开发手册]
 * 【强制】线程池不允许使用 Executors 工具去创建，而是通过 ThreadPoolExecutor 的方式，这样的处理方式让写的同学更加明确线程池的运行规则，规避资源耗尽的风险。
 * 说明：Executors 返回的线程池对象的弊端如下：
 * 1）SingleThreadPool 和 FixedThreadPool，允许的请求队列长度为 Integer.MAX_VALUE，可能会堆积大量的请求，从而导致 OOM。
 * 2）CachedThreadPool 和 ScheduledThreadPool，允许的创建线程数量为 Integer.MAX_VALUE，可能会创建大量的线程，从而导致 OOM。
 * 创建线程的正确姿势：避免使用 Executors 创建线程池，主要是避免使用其中的默认实现，那么我们可以直接调用 ThreadPoolExecutor 的构造器创建线程池，同时设置正确的参数（最大线程数量、任务队列容量等）就可以了。
 * 或者使用开源类库：Apache 或 Guava 等。
 */
public class AExecutors {
    private AExecutors() {
    }

    // --- 快捷创建线程池的 [常用] 方法 --- //

    public static ExecutorService newSingleThreadExecutor() {
        /**
         * 1. 创建一个 [“单一数量” 的线程池]。
         * - 唯一线程（corePoolSize = maximumPoolSize = 1）的存活时间（keepAliveTime = 0L）是无期限的；
         * - 工作队列使用的是基于链表的阻塞队列（LinkedBlockingQueue），是无界的；
         * “单一线程” 的适用场景：
         * - 需要按照提交顺序（FIFO）串行化执行的任务。
         */
        return new Executors.FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(
                        1, 1,
                        0L, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<Runnable>()
                ));
    }

    public static ExecutorService newFixedThreadPool(int nThreads) {
        /**
         * 2. 创建一个 [“固定数量” 的线程池]。
         * - 如果线程数量没有达到 “固定数量（corePoolSize = maximumPoolSize = nThreads）”，每次提交一个新任务就会创建一个新线程进行处理，直到线程数量达到 “固定数量”；
         * - 一旦达到 “固定数量” 就会保持不变（keepAliveTime = 0L），如果某个线程因为执行异常而结束，那么线程池会立即补充一个新线程；
         * - 如果所有线程均处于繁忙状态，新提交的任务会放入工作队列；
         * - 工作队列使用的是基于链表的阻塞队列（LinkedBlockingQueue），是无界的。
         * “固定数量” 的适用场景：
         * - CPU 密集型的，需要长期执行的任务，在 CPU 被工作线程长时间使用的情况下，能确保尽可能少的切换线程。
         * “固定数量” 的弊端：
         * - 当提交大量任务时，（无界）队列无限增长，从而导致 JVM 内存溢出。
         */
        return new ThreadPoolExecutor(
                nThreads, nThreads,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>()
        );
    }

    public static ExecutorService newCachedThreadPool() {
        /**
         * 3.（推荐）创建一个 [“可缓存（不限量）” 的线程池]。
         * - 如果池内线程均处于繁忙状态，就会立即创建一个新的（非核心，corePoolSize = 0）线程来处理新提交的任务；且没有数量限制（Integer.MAX_VALUE）；
         * - 如果（非核心）线程空闲（线程数量超过了任务数量，部分线程无事可干），则会到期（keepAliveTime = 60s）回收。
         * - 工作队列使用的是无缓冲区的同步阻塞队列（SynchronousQueue）。
         * “可缓存（不限量）” 的适用场景：
         * - 需要快速处理（海量）突发性、耗时短的任务，如 Netty 的 NIO 处理场景、REST API 接口的瞬时削峰场景等。
         * “可缓存（不限量）” 的弊端：
         * - 当提交海量任务时，会创建海量线程，从而导致 OS 线程资源耗尽。
         */
        return new ThreadPoolExecutor(
                0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>()
        );
    }

    public static ExecutorService newWorkStealingPool(int parallelism) {
        /**
         * 4. 创建一个 [“（并行）工作窃取” 的线程池]。
         * - 创建一个 ForkJoinPool 线程池，该线程池维护足够的线程以支持给定的并行级别，并可以使用多个队列来减少争用。
         * - 并行级别对应于主动参与或可参与任务处理的线程的最大数量；
         * - 线程的实际数量可能会动态增长和收缩；
         * - “工作窃取” 不保证提交任务的执行顺序。
         * “（并行）工作窃取” 的适用场景：
         * - 当大多数任务生成其他子任务时（就像大多数 ForkJoinTasks 一样），以及当许多小任务从外部客户端提交到池中时，这可以实现高效的处理。
         */
        return new ForkJoinPool(
                parallelism,
                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                null,
                true);
    }

    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        /**
         * 5. 创建一个 [“（延时、周期性）可调度” 的线程池]。
         * [默认配置]
         * - maximumPoolSize     最大线程数，没有数量限制（Integer.MAX_VALUE）；
         * - keepAliveTime       空闲线程存活时间，无期限（keepAliveTime = 0L）；
         * - workQueue          （阻塞）工作队列，无界（DelayedWorkQueue）；
         * - handler             拒绝策略，默认拒绝策略（AbortPolicy）。
         * 所谓 “延时” 指的是当被调度任务的执行时间超过了指定的时间间隔（下一次的调度执行时间）时，ScheduledExecutorService 不会再创建一个新的线程去并发执行这个任务，而是等待前一次调度执行完毕后再重新调度执行，即 “延时” 调度执行。
         * “（延时、周期性）可调度” 的适用场景：
         * - 需要周期性执行的任务。
         */
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }


}
